package cmp.viewer.panels;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.sql.ResultSet;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.SwingConstants;
import javax.swing.event.TableModelEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

import cmp.main.Globals;
import cmp.viewer.ViewerFrame;
import assem.DBConn;
import util.Debug.ErrorReport;
import util.methods.UIHelpers;

public class SequenceViewPanel extends JPanel {
	private static final long serialVersionUID = 4196828062981388452L;
	private static final int NUM_DISPLAY_ROWS = 15;
	private static final int MAX_TABLE_WIDTH = 550;
	final int libCol = 5; // number of columns of lib data
	
	public SequenceViewPanel(ViewerFrame parentFrame, String name, long seqID, int rowNum) {
		theParentFrame = parentFrame;
		setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
		setBackground(theParentFrame.getSettings().getFrameSettings().getBGColor());
		UTid = seqID;
		nCurRow = rowNum;
		theParentFrame = parentFrame;
		PGstr = name.substring(0,name.length()-2); // has a ":" on the end as meant for label on left
		
		try {
			conn = theParentFrame.getDBConnection();
			computeTextArea();
			if (computeTheHitTable()==0) 
				textArea += "\n" + "No database hits";
			else
				createTablePanel();
			createViewPanel();
		}
		catch (Exception e) {ErrorReport.reportError(e, "trying to open View Detail Panel"); }
	}
	
	public String getName() { return UTstr; } // tag on left
	public String getSummary() { return UTstr + " details"; } // summary on Results
	/***************************************************
	 * compute Text Area
	 */
	private void computeTextArea() {
		try {
			String sql = "SELECT UTstr, expListN, expList, ntLen, aaLen " +
					" from unitrans where UTid=" + UTid;
			ResultSet rs = ViewerFrame.executeQuery(conn, sql, null);
			rs.next();
			UTstr = rs.getString(1);
			String norm	= rs.getString(2);
			String raw = rs.getString(3);
			String ntlen = rs.getString(4);
			String aalen = rs.getString(5);
			ViewerFrame.closeResultSet(rs);	// must be closed before starting another query
			
			// if from sequence table, no PGstr
			String pairs = null;
			if (!PGstr.equals("PGSTR")) {
				// pairs containing UTstr and contained in PMstr -- need PMid
				int PGid=0;
				sql = "SELECT PGid FROM pog_groups where PGstr='" + PGstr + "'";
				rs = ViewerFrame.executeQuery(conn, sql, null);
				if (rs.next()) {
					PGid = rs.getInt(1); 
					ViewerFrame.closeResultSet(rs);	
							
					sql = "SELECT UTstr1, UTstr2, PCC,pog_members.PGid " +
						" from pairwise, pog_members " +
						" where pog_members.PGid=" + PGid + 
						" and ( (pairwise.UTid1=" + UTid + " and pairwise.UTid2=pog_members.UTid) " +
						" or (pairwise.UTid2=" + UTid +  " and pairwise.UTid1=pog_members.UTid) )";
	
					rs = ViewerFrame.executeQuery(conn, sql, null);
					while (rs.next()) {
						double d = rs.getDouble(3);
						if (d >= Globals.PCCcutoff) {
							String u1 = rs.getString(1);
							if (u1.equals(UTstr)) u1 = rs.getString(2);
							if (pairs==null) pairs = u1 + " " + Globals.saveDouble(d);
							else pairs += "; " + u1 + " " + Globals.saveDouble(d);
						}
					}
				}
			}
			ViewerFrame.closeResultSet(rs);	
		
			textArea = 
					" " + UTstr + 
					"   Sequence length: " + ntlen + "   Protein length: " + aalen + "\n\n" + 
					"   Counts: " + raw + "\n" +
					"   RPKM  : " + norm + "\n\n";
			if (pairs != null)
					textArea += PGstr + " pairs: " + pairs + "\n";
		}
		catch (Exception e) {
			ErrorReport.reportError(e, "trying to create sequence info"); 
			textArea = "error in processing";
		}
	}
	/********************************************************
	 * Compute Hit table 
	 */
	private int computeTheHitTable() {	
	try {
		if (!conn.hasTable("unitrans_hits")) {
			System.out.println("No unitran hits table");
			return 0;
		}
		theHitData = new ArrayList<HitListData> ();
		int cnt=0;
		String sql = "SELECT " +
				" unitrans_hits.HITid, unitrans_hits.HITstr, " +
				" unitrans_hits.percent_id, unitrans_hits.alignment_len, " +
				" unitrans_hits.seq_start, unitrans_hits.seq_end, " +
				" unitrans_hits.e_value, unitrans_hits.type, " +
				" unique_hits.description, unique_hits.species " + 
				" FROM unitrans_hits " +
				" JOIN unique_hits " + 
				" WHERE unitrans_hits.UTid=" + UTid + 
				" AND unitrans_hits.HITid=unique_hits.HITid";
		ResultSet rs = ViewerFrame.executeQuery(conn, sql, null);
		if (rs == null) ErrorReport.die("null result on database query in computeTheHitTable");
		
		while (rs.next()) {	
			String id = rs.getString(2);
			int pid = rs.getInt(3);
			int len = rs.getInt(4);
			int start = rs.getInt(5);
			int end = rs.getInt(6);		
			double eval = rs.getDouble(7);
			String type = rs.getString(8);
			String desc = rs.getString(9);
			String spec = rs.getString(10);
			
			theHitData.add(new 	HitListData(id, pid, len, start, end,eval, type, desc, spec));
			cnt++;
		}
		ViewerFrame.closeResultSet(rs);
		return cnt;
	}
	catch (Exception e) {ErrorReport.reportError(e, "Creating hit table"); return 0;}
	}
	/**
	 * HitListData class for Hit table
	 * 		order of column headings must be the same as final numbers,
	 * 		and correspond to switches
	 */
	private String [] columnHeadings() { // used by QueryResultsTableModel
		String [] x = {"Name", "Eval","Type", "Description", "Species", "Percent", "Align", "Start", "End"};
		return x;
	}
	private class HitListData {
		public static final int SORT_BY_NAME = 0; 
		public static final int SORT_BY_EVAL = 1; 
		public static final int SORT_BY_TYPE = 2; 
		public static final int SORT_BY_DESC = 3; 
		public static final int SORT_BY_SPEC = 4; 
		public static final int SORT_BY_PERCENT = 5; 
		public static final int SORT_BY_ALIGNLEN = 6;
		public static final int SORT_BY_START = 7; 
		public static final int SORT_BY_END = 8; 
		
		public HitListData(String name, int percent, int len, int start, int end, 
				double eval, String t, String d, String s) {
			hitID = name;
			nPercent = percent;
			nAlignLen = len;
			nStart = start;
			nEnd = end;
			dEVal = eval;
			type = t;
			desc = d;
			species = s;
		}
		
		public Object getValueAt(int pos) {
			switch(pos) {
			case 0: return hitID;
			case 1:
				if(dEVal == 0)
					return new String("0.0");
				return (new DecimalFormat("0.0E0")).format((Double)dEVal); 
			case 2: return type;
			case 3: return desc;
			case 4: return species;
			case 5: return nPercent;
			case 6: return nAlignLen;
			case 7: return nStart;
			case 8: return nEnd;
			}
			return null;
		}
		
		public int compareTo(HitListData obj, boolean sortAsc, int field) {
			int order = 1;
			if(!sortAsc) order = -1;
			
			switch(field) {
			case SORT_BY_NAME: return order * hitID.compareTo(obj.hitID);
			case SORT_BY_TYPE: return order * type.compareTo(obj.type);
			case SORT_BY_DESC: return order * desc.compareTo(obj.desc);
			case SORT_BY_SPEC: return order * species.compareTo(obj.species);
			case SORT_BY_EVAL: return order * (new Double(dEVal)).compareTo(new Double(obj.dEVal));
			case SORT_BY_PERCENT: return order * (new Integer(nPercent)).compareTo(new Integer(obj.nPercent));
			case SORT_BY_ALIGNLEN: return order * (new Integer(nAlignLen)).compareTo(new Integer(obj.nAlignLen));
			case SORT_BY_START: return order * (new Integer(nStart)).compareTo(new Integer(obj.nStart));
			case SORT_BY_END: return order * (new Integer(nEnd)).compareTo(new Integer(obj.nEnd));
			}
			return 0;
		}
		private String hitID = "";
		private int nPercent;
		private int nAlignLen;
		private int nStart = -1;
		private int nEnd = -1;
		private double dEVal = 0;
		private String type = "";
		private String desc = "";
		private String species = "";
	} // end HitData class
	 
	/**********************************************
	 * create panel - with textArea and hitTable
	 **/
	private void createViewPanel() {
		mainPanel = new JPanel();
		mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS));
		mainPanel.setBackground(theParentFrame.getSettings().getFrameSettings().getBGColor());	
	
		textAreaTop = new JTextArea ( textArea );
		textAreaTop.setEditable( false );		
		textAreaTop.setFont(new Font("monospaced", Font.PLAIN, 12));
		int nTextWidth = Math.max((int)textAreaTop.
				getPreferredSize().getWidth() + 5, 600);		
		textAreaTop.setMaximumSize( new Dimension ( nTextWidth, 
				(int)textAreaTop.getPreferredSize().getHeight() ) );			
		
		mainScroll = new JScrollPane ( mainPanel );
		mainScroll.setHorizontalScrollBarPolicy( JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED );
		mainScroll.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED );
		mainScroll.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
		UIHelpers.setScrollIncrements( mainScroll );
        
		JPanel tableAndHeader = new JPanel ();
		tableAndHeader.setLayout( new BoxLayout ( tableAndHeader, BoxLayout.PAGE_AXIS ) );
		tableAndHeader.setBackground( Globals.BGCOLOR );
		textAreaTop.setAlignmentX(LEFT_ALIGNMENT);
		tableAndHeader.add ( textAreaTop );	
		if(theHitTable != null) 
			tableAndHeader.add ( tableScroll );	
		tableAndHeader.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
		mainPanel.add( tableAndHeader );
		mainPanel.add( Box.createVerticalStrut(10) );			
		invalidate();
		mainPanel.add ( Box.createVerticalGlue() );	
		setLayout( new BoxLayout ( this, BoxLayout.PAGE_AXIS ) );
		//add ( createToolbar () );
		add ( Box.createVerticalStrut(5) );
		add ( mainScroll );	
	}
	/*****************************************************************
	 * create tool bar at the top of the panel
	 */
	private JPanel createToolbar ( )
	{	
		return null;
	}
	/** 
	 * Generic table stuff -- should not need changing 
	 ***/
	private void createTablePanel() {
		theHitTable = new JTable();
		theHitTable.setColumnSelectionAllowed( false );
		theHitTable.setCellSelectionEnabled( false );
		theHitTable.setRowSelectionAllowed( true );
		theHitTable.setShowHorizontalLines( false );
		theHitTable.setShowVerticalLines( false );	
		theHitTable.setIntercellSpacing ( new Dimension ( 1, 0 ) );
		theModel = new QueryResultsTableModel(theHitData);
		theHitTable.setModel(theModel);
	
		JTableHeader header = theHitTable.getTableHeader();
		header.setUpdateTableInRealTime(true);
		header.addMouseListener(theModel.new ColumnListener(theHitTable));
		header.setReorderingAllowed(true);
		resizeColumns();
		
		tableScroll = new JScrollPane(theHitTable);
		tableScroll.setBorder( null );
		Dimension size = theHitTable.getPreferredSize();
		size.height = NUM_DISPLAY_ROWS * theHitTable.getRowHeight(); 
		size.width = Math.min(MAX_TABLE_WIDTH, theHitTable.getWidth());
		tableScroll.setPreferredSize(size);
		tableScroll.getViewport().setBackground(Globals.BGCOLOR);
		tableScroll.setAlignmentX(LEFT_ALIGNMENT);			
	}
	
	private class QueryResultsTableModel extends AbstractTableModel {
		private static final long serialVersionUID = 774460555629612058L;
		
		private String [] theLabels = null;
		
		public QueryResultsTableModel(ArrayList<HitListData> displayValues) { 
			theDisplayValues = displayValues; 
			theLabels = columnHeadings();
		}
		public int getColumnCount() { return theLabels.length; }
        public int getRowCount() { return theDisplayValues.size(); }
        public Object getValueAt(int row, int col) { 
        	return theDisplayValues.get(row).getValueAt(col); }
        public String getColumnName(int col) { return theLabels[col]; }
        
        private ArrayList<HitListData> theDisplayValues = null;
		private boolean sortAsc = true;
		private int sortMode = HitListData.SORT_BY_EVAL;
        
  	  	public class ColumnListener extends MouseAdapter {
		    protected JTable table;

		    public ColumnListener(JTable t) {
		    		table = t;
		    }
		    public void mouseClicked(MouseEvent e) {
			    	TableColumnModel colModel = table.getColumnModel();
			    	int columnModelIndex = colModel.getColumnIndexAtX(e.getX());
			    	int modelIndex = colModel.getColumn(columnModelIndex).getModelIndex();
			    	if (modelIndex < 0) return;
			    	
			    	if (sortMode == modelIndex) sortAsc = !sortAsc;
			    	else sortMode = modelIndex;
	
			    	for (int i = 0; i < table.getColumnCount(); i++) { 
			    		TableColumn column = colModel.getColumn(i);
			    		column.setHeaderValue(getColumnName(column.getModelIndex()));
			    	}
			    	table.getTableHeader().repaint();
			    	Collections.sort(theDisplayValues, new HitListComparator(sortAsc, sortMode));
			    	table.tableChanged(new TableModelEvent(QueryResultsTableModel.this));
			    	table.repaint();
		    }
  	  	}
	}
	private class HitListComparator implements Comparator<HitListData> {
		public HitListComparator(boolean sortAsc, int mode) {
			bSortAsc = sortAsc;
			nMode = mode;
		}		
		public int compare(HitListData obj1, HitListData obj2) { 
			return obj1.compareTo(obj2, bSortAsc, nMode); }		
		private boolean bSortAsc;
		private int nMode;
	}	
	public int cntSelectedHits() {
		if(theHitTable == null) return 0;
		int [] selections = theHitTable.getSelectedRows();
		return selections.length;
	}
	public String [] getSelectedHits() {
		int [] selections = theHitTable.getSelectedRows();

		Vector<String> retVal = new Vector<String> ();
		for(int x=0; x<selections.length; x++) {
			String hitID = theHitData.get(selections[x]).hitID;
	
			if(!retVal.contains(hitID)) retVal.add(hitID);
		}	
		return retVal.toArray(new String[0]);
	}
	private void resizeColumns() {
		theHitTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		int margin = 2;
		
		for(int x=0; x < theHitTable.getColumnCount(); x++) {
			DefaultTableColumnModel colModel = (DefaultTableColumnModel) 
				theHitTable.getColumnModel();
			TableColumn col = colModel.getColumn(x);
			int maxSize = 0;		
			TableCellRenderer renderer = col.getHeaderRenderer();
			if(renderer == null)
				renderer = theHitTable.getTableHeader().getDefaultRenderer();
			Component comp = renderer.getTableCellRendererComponent(theHitTable, 
					col.getHeaderValue(), false, false, 0, 0);
			maxSize = comp.getPreferredSize().width;
			
			for(int y=0; y<theHitTable.getRowCount(); y++) {
				renderer = theHitTable.getCellRenderer(y, x);
				comp = renderer.getTableCellRendererComponent(theHitTable, 
						theHitTable.getValueAt(y, x), false, false, y, x);
				maxSize = Math.max(maxSize, comp.getPreferredSize().width);
			}
			maxSize += 2 * margin;			
			col.setPreferredWidth(maxSize);
		}
		((DefaultTableCellRenderer) 
				theHitTable.getTableHeader().getDefaultRenderer()).setHorizontalAlignment(SwingConstants.LEFT);
	}	
	public void mousePressed(MouseEvent e) {}
	public void mouseReleased(MouseEvent e) {}
	public void mouseEntered(MouseEvent e) {}
	public void mouseExited(MouseEvent e) {}
	public void mouseClicked(MouseEvent e) {}
	/************  End Hit table code *************************/
	
	// data types
	private String textArea = "";
	private QueryResultsTableModel theModel = null;
	private ArrayList<HitListData> theHitData = null;
	
	// Views
	private JPanel mainPanel = null;
	private JTextArea textAreaTop = null;
	private JTable theHitTable = null;
	private JScrollPane mainScroll = null;
	private JScrollPane tableScroll = null;
	
	// parameters
	private ViewerFrame theParentFrame = null;
	private long UTid = -1;
	private int nCurRow = -1;
	private String PGstr = "";
	private DBConn conn = null;
	
	// for tag on left panel
	private String UTstr="";
}
